/******************************************************************************
 * Copyright (C) 671643387@qq.com
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 *****************************************************************************/

#include "Object.h"
#include "Shared/Log/Log.h"

Object::Object(void)
    : ObjectType_(TYPEMASK_OBJECT)
    , ObjectTypeId_(TYPEID_OBJECT)
    , ChangedValues_()
    , ValuesCount_(0)
    , ObjectUpdated_(false)
    , InWorld_(false)
    , PackGUID_()
{

}

Object::~Object(void)
{

}

void Object::SetUInt32Value(std::uint16_t _Index, std::uint32_t _Value)
{
    NODECPP_ASSERT(_Index < ValuesCount_ || PrintIndexError(_Index, true));

    if (UInt32Values_[_Index] != _Value)
    {
        UInt32Values_[_Index] = _Value;
        ChangedValues_[_Index] = true;
        _MarkForClientUpdate();
    }
}

void Object::SetUInt64Value(std::uint16_t _Index, const std::uint64_t& _Value)
{
	NODECPP_ASSERT(std::uint32_t(_Index + 1) < ValuesCount_ 
		|| PrintIndexError(_Index, true));
    if (*((std::uint64_t*)& (UInt32Values_[_Index])) != _Value)
    {
        UInt32Values_[_Index] = *((std::uint32_t*)&_Value);
        UInt32Values_[_Index + 1] = *(((std::uint32_t*)&_Value) + 1);
        ChangedValues_[_Index] = true;
        ChangedValues_[_Index + 1] = true;
        _MarkForClientUpdate();
    }
}

const std::uint64_t& Object::GetUInt64Value(std::uint16_t _Index) const
{
	NODECPP_ASSERT(std::uint32_t(_Index + 1) < ValuesCount_ 
		|| PrintIndexError(_Index, false));
	return *((std::uint64_t*)& (UInt32Values_[_Index]));
}

void Object::AddToClientUpdateList()
{
    sLog.outError("Unexpected call of Object::AddToClientUpdateList for object "
        "(TypeId: %u Update fields: %u)", GetTypeId(), ValuesCount_);
    NODECPP_ASSERT(false);
}

void Object::_MarkForClientUpdate()
{
    if (InWorld_)  {
        if (!ObjectUpdated_) {
            AddToClientUpdateList();
            ObjectUpdated_ = true;
        }
    }
}

bool Object::PrintIndexError(std::uint32_t _Index, bool _Set) const
{
    sLog.outError("Attempt %s nonexistent value field: %u (count: %u) "
        "for object typeid: %u type mask: %u", (_Set ? "set value to" : "get value from"),
        _Index, ValuesCount_, GetTypeId(), ObjectType_);
    return false;
}

void Object::_InitValues()
{
    UInt32Values_ = new std::uint32_t[ValuesCount_];
    memset(UInt32Values_, 0, ValuesCount_ * sizeof(std::uint32_t));
    ChangedValues_.resize(ValuesCount_, false);
    ObjectUpdated_ = false;
}

void Object::_Create(std::uint32_t _Guidlow, std::uint32_t _Entry, HighGuid _Guidhigh)
{
    if (ValuesCount_ != 0) {
        _InitValues();
    }

    ObjectGuid _Guid = ObjectGuid(_Guidhigh, _Entry, _Guidlow);
    SetGuidValue(OBJECT_FIELD_GUID, _Guid);
    SetUInt32Value(OBJECT_FIELD_TYPE, ObjectType_);
    PackGUID_.Set(_Guid);
}